<script lang="tsx">
import { computed, defineComponent, type PropType, ref, unref } from 'vue';
import type { FormActionType, FormProps, FormSchema, Nullable, Recordable } from '../types/form';
import { componentMap } from '../componentMap';
import { cloneDeep, get, isBoolean, isFunction, isNull, upperFirst } from 'lodash-es';
import { createPlaceholderMessage, getSlot } from '../common/helper';
import type { FormItemRule } from 'element-plus';
import { ElCol, ElDivider, ElFormItem } from 'element-plus';
import { setComponentRuleType } from '../common/gloab';
import { getOptions } from '../hooks/useFormEvents';
export default defineComponent({
  name: 'SzFormItem',
  props: {
    //当前数参数
    schema: {
      type: Object as PropType<FormSchema>,
      default: () => ({}),
    },
    // form 表单相关参数
    formProps: {
      type: Object as PropType<FormProps>,
      default: () => ({}),
    },
    // 设置FormModel函数
    setFormModel: {
      type: Function as PropType<
        (key: string, value: any, schema: FormSchema, isValid?: boolean) => void
      >,
      default: null,
    },
    // FormModel
    formModel: {
      type: Object as PropType<Recordable>,
      default: () => ({}),
    },
    // 所有默认值
    allDefaultValues: {
      type: Object as PropType<Recordable>,
      default: () => ({}),
    },
    formActionType: {
      type: Object as PropType<Partial<FormActionType>>,
    },
  },
  setup(props, { slots }) {
    let modelValue: any = ref<any>();
    modelValue.value = props.schema.defaultValue
      ? JSON.parse(JSON.stringify(props.schema.defaultValue))
      : '';

    // 获取部分默认值
    const getValues = computed(() => {
      const { allDefaultValues, formModel, schema } = props;
      const { mergeDynamicData } = props.formProps;
      return {
        field: schema.field,
        model: formModel,
        values: {
          ...mergeDynamicData,
          ...allDefaultValues,
          ...formModel,
        } as Recordable,
        schema: schema,
      };
    });

    // 是否显示相关
    function getShow(): { isShow: boolean; isIfShow: boolean } {
      const { show, ifShow } = props.schema;

      let isShow = true;
      let isIfShow = true;

      if (isFunction(show)) {
        isShow = show(unref(getValues));
      } else if (isBoolean(show)) {
        isShow = show;
      }
      if (isFunction(ifShow)) {
        isIfShow = ifShow(unref(getValues));
      } else if (isBoolean(ifShow)) {
        isIfShow = ifShow;
      }
      return { isShow, isIfShow };
    }

    // input 等组件相关参数
    const getComponentsProps = computed(() => {
      const { schema, formModel, formActionType } = props;
      let { componentProps = {} } = schema;
      // 是否是函数
      if (isFunction(componentProps)) {
        // console.log('是否是函数');
        componentProps = componentProps({ schema, formModel, formActionType, isInit: false }) ?? {};
      }
      // Divider 时参数
      if (schema.component === 'Divider') {
        componentProps = {
          ...{ direction: 'horizontal', borderStyle: 'solid', contentPosition: 'center' },
          ...componentProps,
        };
      } else if (schema.component === 'SelectV2') {
        componentProps = {
          ...componentProps,
          options: getOptions.value({ options: [], ...componentProps }),
        };
      }

      return componentProps as Recordable;
    });

    // 验证规则
    function handleRules(): FormItemRule[] {
      const {
        rules: defRules = [],
        component,
        rulesMessageJoinLabel,
        label,
        dynamicRules,
        required,
      } = props.schema;
      // 动态判返当前组件你校验规则
      if (isFunction(dynamicRules)) {
        return dynamicRules(unref(getValues)) as FormItemRule[];
      }

      let rules: FormItemRule[] | any = cloneDeep(defRules) as FormItemRule[];
      const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
      // 是否包含
      const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
        ? rulesMessageJoinLabel
        : globalRulesMessageJoinLabel;

      const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;

      function validator(rule: any, value: any, callback: any) {
        const msg = rule.message || defaultMsg;
        if (value === undefined || isNull(value)) {
          // 空值
          return callback(new Error(msg));
        } else if (Array.isArray(value) && value.length === 0) {
          // 数组类型
          return callback(new Error(msg));
        } else if (typeof value === 'string' && value.trim() === '') {
          // 空字符串
          return callback(new Error(msg));
        } else if (
          typeof value === 'object' &&
          Reflect.has(value, 'checked') &&
          Reflect.has(value, 'halfChecked') &&
          Array.isArray(value.checked) &&
          Array.isArray(value.halfChecked) &&
          value.checked.length === 0 &&
          value.halfChecked.length === 0
        ) {
          // 非关联选择的tree组件
          return callback(new Error(msg));
        }
        return callback();
      }

      const getRequired = isFunction(required) ? required(unref(getValues)) : required;

      /*
       * 1、若设置了required属性，又没有其他的rules，就创建一个验证规则；
       * 2、若设置了required属性，又存在其他的rules，则只rules中不存在required属性时，才添加验证required的规则
       *     也就是说rules中的required，优先级大于required
       */
      if (getRequired) {
        if (!rules || rules.length === 0) {
          rules = [{ required: getRequired, validator }];
        } else {
          const requiredIndex: number = rules.findIndex((rule: object) =>
            Reflect.has(rule, 'required'),
          );

          if (requiredIndex === -1) {
            rules.push({ required: getRequired, validator });
          }
        }
      }

      const requiredRuleIndex: number = rules.findIndex(
        (rule: object) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'),
      );

      if (requiredRuleIndex !== -1) {
        const rule: any = rules[requiredRuleIndex];
        const { isShow } = getShow();
        if (!isShow) {
          rule.required = false;
        }
        if (component) {
          if (!Reflect.has(rule, 'type')) {
            rule.type = component === 'InputNumber' ? 'number' : 'string';
          }

          rule.message = rule.message || defaultMsg;

          if (component.includes('Input') || component.includes('Textarea')) {
            rule.whitespace = true;
          }
          const valueFormat = unref(getComponentsProps)?.valueFormat;
          // 设置 rule.type
          setComponentRuleType(rule, component, valueFormat);
        }
      }

      // Maximum input length rule check
      const characterInx = rules.findIndex((val: { max: any }) => val.max);
      if (characterInx !== -1 && !rules[characterInx].validator) {
        // eslint-disable-next-line no-self-assign
        rules[characterInx].message = rules[characterInx].message;
      }

      return rules;
    }

    // 是否禁用
    const getDisable = computed(() => {
      const { disabled: globDisabled } = props.formProps;
      const { disabled: itemDisabled = false } = unref(getComponentsProps);
      let disabled = !!globDisabled || itemDisabled;
      return disabled;
    });

    // 渲染组件相关
    function renderComponent() {
      let {
        component,
        field,
        fieldTwo = '',
        changeEvent = 'change',
        label,
        renderComponentContent,
        rulesMessageJoinLabel,
      } = props.schema;
      const { size } = props.formProps;
      const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
      const propsData: Recordable = {
        getPopupContainer: (trigger: Element) => trigger.parentNode,
        size,
        ...unref(getComponentsProps),
        disabled: unref(getDisable),
      };
      const eventKey = `on${upperFirst(changeEvent)}`;
      // console.log('eventKey', eventKey);

      // 事件
      const on = {
        [eventKey]: (...args: Nullable<Recordable>[]) => {
          if (propsData[eventKey]) {
            propsData[eventKey](...args);
          }
        },
      };

      // 组件
      const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

      // placeholder 提示
      // 是否包含label
      const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
        ? rulesMessageJoinLabel
        : globalRulesMessageJoinLabel;
      const placeholder = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;
      propsData.placeholder = unref(getComponentsProps)?.placeholder || placeholder;

      propsData.codeField = field;
      propsData.formValues = unref(getValues);

      // 绑定值
      const compAttr: Recordable = {
        ...propsData,
        ...on,
      };

      const itemVal = computed({
        get: () => {
          let valued = get(props.formModel, field);
          return valued;
        },
        set: (val) => {
          props.setFormModel(field, val, props.schema);
        },
      });
      const itemValTwo = computed({
        get: () => {
          return get(props.formModel, fieldTwo);
        },
        set: (val) => {
          props.setFormModel(fieldTwo, val, props.schema, false);
        },
      });

      // 自定义渲染组内部的 slot
      if (!renderComponentContent) {
        return (
          <Comp
            {...compAttr}
            v-model:modelValue={itemVal.value}
            v-model:modelName={itemValTwo.value}
          />
        );
      }
      const compSlot = isFunction(renderComponentContent)
        ? renderComponentContent(unref(getValues))
        : {
            default: () => renderComponentContent,
          };

      return (
        <Comp {...compAttr} v-model:modelValue={itemVal.value} v-model:modelName={itemValTwo.value}>
          {compSlot}
        </Comp>
      );
    }

    // formitem 组件相关
    function renderItem() {
      const { slot, field, component, label, labelWidth, render, suffix, prefix, colOrRow } =
        props.schema;
      const { labelWidth: fLabelWidth, labelPosition } = props.formProps;

      if (component === 'Divider') {
        return (
          <ElCol span={24}>
            <ElDivider {...unref(getComponentsProps)} />
          </ElCol>
        );
      } else {
        const itemProps = {
          prop: field,
          label,
          labelWidth: labelWidth ?? fLabelWidth,
          labelPosition,
        };
        // 渲染组件
        const getContent = () => {
          let renderd: any;
          if (slot) {
            renderd = getSlot(slots, slot, unref(getValues));
          } else if (render) {
            renderd = isFunction(render) ? render(unref(getValues)) : render;
          } else {
            renderd = renderComponent();
          }
          return renderd;
        };
        // 组件前后内容
        const showSuffix = !!suffix;
        const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
        const showPrefix = !!prefix;
        const getPrefix = isFunction(prefix) ? prefix(unref(getValues)) : prefix;
        return (
          <ElFormItem {...itemProps} rules={handleRules()}>
            <div {...{ class: ['flex', 'flex-1', colOrRow == 'col' ? 'flex-col' : 'flex-row'] }}>
              {showPrefix && <span class="prefix">{getPrefix}</span>}
              <div class="flex-1"> {getContent()}</div>
              {showSuffix && <span class="suffix">{getSuffix}</span>}
            </div>
          </ElFormItem>
        );
      }
    }

    return () => {
      const { colSlot, component, colProps, renderColContent } = props.schema;
      const { baseColProps = {} } = props.formProps;
      // 当前组件是否在componentMap中
      if (!componentMap.has(component)) {
        return null;
      }
      const { isIfShow, isShow } = getShow();
      const values = unref(getValues);
      //   插槽
      const getContent = () => {
        let render: any;

        if (colSlot) {
          render = getSlot(slots, colSlot, values);
        } else if (renderColContent) {
          render = isFunction(renderColContent) ? renderColContent(values) : renderColContent;
        } else {
          render = renderItem();
        }
        return render;
      };

      const realColProps = { ...baseColProps, ...colProps };
      return (
        isIfShow && (
          <ElCol v-show={isShow} {...realColProps}>
            {getContent()}
          </ElCol>
        )
      );
    };
  },
});
</script>
<style lang="scss" scoped>
::v-deep(.el-date-editor.el-input__wrapper) {
  width: 100%;
}
</style>
